﻿//#include <Windows.h>
#include "Scene.h"
#include "Vector3d.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <limits>
#include <stdlib.h>
#include <time.h>

#define EPS 10e-5

using namespace std;

Scene::Scene() {
}

Scene::~Scene() {
	delete[] triangles;
	delete[] lights;
	delete[] attributes;
}

void Scene::readGeometry(string filePath) {
	int vertSize = NULL;
	ifstream file;
	string line, token;
	stringstream lineStream;
	vector<Point3d> vertices;
	x_min_domain = y_min_domain = z_min_domain = numeric_limits<float>::infinity();
	x_max_domain = y_max_domain = z_max_domain = - numeric_limits<float>::infinity();
	
	file.open(filePath.c_str());

	if (file.is_open()) {
		
		while (!file.eof()) {
			
			getline(file, line);
			lineStream.clear();
			lineStream.str(line);
			
			while (lineStream >> token) {
				
				if (token == "vertices" && !lineStream.eof()) {
					lineStream >> vertSize;
					vertices = vector<Point3d>(vertSize);
					
					for (int i=0; i<vertSize; i++) {
						getline(file, line);
						lineStream.clear();
						lineStream.str(line);
						
						float t1, t2, t3;
						lineStream >> t1;
						lineStream >> t2;
						lineStream >> t3;

						x_min_domain = t1<x_min_domain?t1:x_min_domain;
						y_min_domain = t2<y_min_domain?t2:y_min_domain;
						z_min_domain = t3<z_min_domain?t3:z_min_domain;

						x_max_domain = t1>x_max_domain?t1:x_max_domain;
						y_max_domain = t2>y_max_domain?t2:y_max_domain;
						z_max_domain = t3>z_max_domain?t3:z_max_domain;
						
						vertices[i] = Point3d(t1, t2, t3);
					}
				}
				
				if (token == "triangles" && !lineStream.eof()) {
					lineStream >> trianSize;
					triangles = new Triangle [trianSize];
					
					for (int i=0; i<trianSize; i++) {
						getline(file, line);
						lineStream.clear();
						lineStream.str(line);
						int v1, v2, v3;
						lineStream >> v1;
						lineStream >> v2;
						lineStream >> v3;
						
						triangles[i] = Triangle(vertices[v1], vertices[v2], vertices[v3], i);
					}
				}

				if (token == "parts" && !lineStream.eof()) {

					int i=0;
					int ind;

					while (i<trianSize) {
						getline(file, line);
						lineStream.clear();
						lineStream.str(line);

						while (lineStream >> ind) {
							triangles[i].setAtributeIndex(ind);
							i++;
						}
					}
				}
			}			
		}
	}
	file.close();

	domain_depth = z_max_domain - z_min_domain;
	domain_height = y_max_domain - y_min_domain;
	domain_width = x_max_domain - x_min_domain;
}

void Scene::readCamera(string filePath) {
	ifstream file;
	string line, token;
	stringstream lineStream;
	Point3d cameraCenter, topLeft, bottomLeft, topRight;
	int xRes=-1, yRes=-1;

	file.open(filePath.c_str());

	if (file.is_open()) {
		while(!file.eof()) {

			getline(file, line);
			lineStream.clear();
			lineStream.str(line);
			while (lineStream >> token) {

				if (token == "viewpoint")
				{
					float x, y, z;
					lineStream >> x;
					lineStream >> y;
					lineStream >> z;
					cameraCenter = Point3d(x, y, z);
				}

				if (token == "screen")
				{
					getline(file, line);
					lineStream.clear();
					lineStream.str(line);
					float x, y, z;
					lineStream >> x;
					lineStream >> y;
					lineStream >> z;
					topLeft = Point3d(x, y, z);

					getline(file, line);
					lineStream.clear();
					lineStream.str(line);
					lineStream >> x;
					lineStream >> y;
					lineStream >> z;
					bottomLeft = Point3d(x, y, z);

					getline(file, line);
					lineStream.clear();
					lineStream.str(line);
					lineStream >> x;
					lineStream >> y;
					lineStream >> z;
					topRight = Point3d(x, y, z);
				}

				if (token == "resolution")
				{
					lineStream >> xRes;
					lineStream >> yRes;
				}
			}
		}
	}
	file.close();
	camera = Camera(cameraCenter, topLeft, bottomLeft, topRight, xRes, yRes);
}

void Scene::readLighting(string filePath)
{
	ifstream file;
	string line, token;
	stringstream lineStream;
	float x, y, z, flux, r, g, b;

	file.open(filePath.c_str());

	if (file.is_open()) {
		while(!file.eof()) {

			getline(file, line);
			lineStream.clear();
			lineStream.str(line);
			while(lineStream >> token) {

				if (token == "lights")
				{
					getline(file, line);
					lineStream.clear();
					lineStream.str(line);

					lineStream >> lightNumber;
					lights = new LightSource[lightNumber];
				}

				if (token == "Position")
				{
					for (int i=0; i<lightNumber; i++)
					{
						getline(file, line);
						lineStream.clear();
						lineStream.str(line);

						lineStream >> x;
						lineStream >> y;
						lineStream >> z;
						lineStream >> flux;
						lineStream >> r;
						lineStream >> g;
						lineStream >> b;

						lights[i] = LightSource(x, y, z, flux, r, g, b);
					}
				}
			}
		}
	}
	file.close();
}

void Scene::readAttributes(string filePath) {
	ifstream file;
	string line, token;
	stringstream lineStream;

	float kd, ks, wg, ka, r, g, b, kt, eta, kr;

	file.open(filePath.c_str());

	if (file.is_open())
	{
		while (!file.eof())
		{
			getline(file, line);
			lineStream.clear();
			lineStream.str(line);
			while (lineStream >> token)
			{
				if (token == "attribute")
				{
					getline(file, line);
					lineStream.clear();
					lineStream.str(line);

					lineStream >> attributesNuber;

					attributes = new Attrib[attributesNuber];
				}

				if (token == "attributes")
				{
					for (int i=0; i<attributesNuber; i++) {
						getline(file, line);
						lineStream.clear();
						lineStream.str(line);

						lineStream >> kd; lineStream >> ks; lineStream >> wg; lineStream >> ka;
						lineStream >> r; lineStream >> g; lineStream >> b;

						getline(file, line);
						lineStream.clear();
						lineStream.str(line);

						lineStream >> kt; lineStream >> eta; lineStream >> kr;

						attributes[i] = Attrib(kd, ks, wg, ka, r, g, b, kt, eta, kr);
					}
				}
			}
		}
	}
}

void Scene::readAttributesAlt(string filePath) {
	ifstream file;
	string line, token;
	stringstream lineStream;

	float kd, ks, wg, ka, r, g, b, kt, eta, kr;

	file.open(filePath.c_str());

	if (file.is_open())
	{
		getline(file, line);
		lineStream.clear();
		lineStream.str(line);

		lineStream >> attributesNuber;
		attributes = new Attrib[attributesNuber];
		while(!file.eof())
		{
			for (int i=0; i<attributesNuber; i++) {
				getline(file, line);
				lineStream.clear();
				lineStream.str(line);
				while(lineStream>>token) {
					if (token=="kd") {
						lineStream >> kd;
					} else if (token=="ks") {
						lineStream >> ks;
					} else if (token=="gs") {
						lineStream >> wg;
					} else if (token=="color") {
						lineStream >> r;
						lineStream >> g;
						lineStream >> b;
					} else if (token=="ktd") {
						lineStream >> kt;
					} else if (token=="eta") {
						lineStream >> eta;
					} else if (token=="kf") {
						lineStream >> kr;
					} else if (token=="ka") {
						lineStream >> ka;
					} else if (token=="enddef") {
						attributes[i] = Attrib(kd, ks, wg, ka, r, g, b, kt, eta, kr);
						break;
					}
					getline(file, line);
					lineStream.clear();
					lineStream.str(line);
				}
				getline(file, line);
			}
		}
	}
}

void Scene::voxelize() {
	float v;
	float x, y, z;
	vector<vector<vector<list<int>>>> tmpList;
	//Wyznaczenie mnożnika dla odpowiednich kierunków, aby określić gęstość wokselizacji
	v = pow((trianSize)/(domain_width*domain_height*domain_depth), 1.f/3.f);
	
	//Obliczenie ilości wokseli w każdym kierunku
	mx = ceil(v*domain_width) * 3;
	my = ceil(v*domain_height) * 3;
	mz = ceil(v*domain_depth) * 3;

	//Obliczenie wysokości, szerokości oraz długości pojedynczego woksela
	dx = domain_width/mx;
	dy = domain_height/my;
	dz = domain_depth/mz;
	voxel_half_legths = Vector3d(dx/2, dy/2, dz/2, false);

	//Ustalenie wspolrzednych srodka pierwszego woksela
	x = x_min_domain + voxel_half_legths.x;
	//y = y_min_domain + voxel_half_legths.y;
	//z = z_min_domain + voxel_half_legths.z;

	//////////////////////////////////////////////////////////////////////////
	// Tworzenie siatki wokseli
	//////////////////////////////////////////////////////////////////////////
	voxels = new Voxel**[mx];
	for (int i=0; i<mx; i++)
	{
		voxels[i] = new Voxel*[my];
		for (int j=0; j<my; j++)
		{
			voxels[i][j] = new Voxel[mz];
		}
	}

	for (int i=0; i<mx; i++) {
		y = y_min_domain + voxel_half_legths.y;
		for (int j=0; j<my; j++) {
			z = z_min_domain + voxel_half_legths.z;
			for (int k=0; k<mz; k++) {
				//Wyznacz punk środka woksela (i, j, k)
				voxels[i][j][k].center = Point3d(x, y, z);
				z += dz;
			}
			y += dy;
		}
		x += dx;
	}

	tmpList.resize(mx);
	for(int q=0; q<mx; q++) {
		tmpList[q].resize(my);
		for (int w=0; w<my; w++) {
			tmpList[q][w].resize(mz);
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Siatka wokseli gotowa, wyznaczone punkty centralne wszystkich wokseli,
	// wyznaczony wektor dlugosci polowkowych (jeden dla kazdego woksela, ze
	// wzgledu na podzial jednorodny, trzymany w klasie scena).
	//////////////////////////////////////////////////////////////////////////

	//cout << voxels[0][0][0].center<< voxels[14][9][29].center;
	//cout << dx << " " << dy << " " << dz << endl;
	//cout << mx << " " << my << " " << mz << endl;

	//////////////////////////////////////////////////////////////////////////
	// Pakowanie trójkątów do wokseli
	//////////////////////////////////////////////////////////////////////////
	
	Triangle* trians = triangles;
	Voxel* curr_vox;
	float max_x, max_y, max_z, min_x, min_y, min_z;
	int i1, j1, k1, i_max, j_max, k_max;
	for (int ind=0; ind<trianSize; ind++, trians++) {
		//Dla każdego trójkąta znajdź jego AABB
		max_x = (trians->p1.x>trians->p2.x && trians->p1.x>trians->p3.x)?trians->p1.x:(trians->p2.x>trians->p3.x?trians->p2.x:trians->p3.x);
		max_y = (trians->p1.y>trians->p2.y && trians->p1.y>trians->p3.y)?trians->p1.y:(trians->p2.y>trians->p3.y?trians->p2.y:trians->p3.y);
		max_z = (trians->p1.z>trians->p2.z && trians->p1.z>trians->p3.z)?trians->p1.z:(trians->p2.z>trians->p3.z?trians->p2.z:trians->p3.z);

		min_x = (trians->p1.x<trians->p2.x && trians->p1.x<trians->p3.x)?trians->p1.x:(trians->p2.x<trians->p3.x?trians->p2.x:trians->p3.x);
		min_y = (trians->p1.y<trians->p2.y && trians->p1.y<trians->p3.y)?trians->p1.y:(trians->p2.y<trians->p3.y?trians->p2.y:trians->p3.y);
		min_z = (trians->p1.z<trians->p2.z && trians->p1.z<trians->p3.z)?trians->p1.z:(trians->p2.z<trians->p3.z?trians->p2.z:trians->p3.z);

		//Wyznacz woksele, do ktorych potencjalnie mozna upakowac trojkat
		i1 = (min_x - x_min_domain)/dx;
		j1 = (min_y - y_min_domain)/dy;
		k1 = (min_z - z_min_domain)/dz;

		i_max = (max_x - x_min_domain)/dx;
		j_max = (max_y - y_min_domain)/dy;
		k_max = (max_z - z_min_domain)/dz;

		//Dla współrzędnych równych krawędziom domeny algorytm chcialby zapakować
		//trójkąty do wokseli o 1 za daleko, więc należy je uciąć
		if (i1>=mx) i1 = mx-1; if (j1>=my) j1 = my-1; if (k1>=mz) k1 = mz-1;
		if (i_max>=mx) i_max = mx-1; if (j_max>=my) j_max = my-1; if (k_max>=mz) k_max = mz-1; 

		//dla danego trójkąta indeksy potencjalnych wokseli wyznaczone,
		//należy sprawdzić, czy faktycznie dany trójkąt należy do wyznaczonych
		//wokseli. (jesli dwa lub trzy indeksy sa takie same, to nie trzeba
		//nic sprawdzac, trojkat lezy w wyznaczonych wokselach
		for (;i1<=i_max; i1++) {
			for(int j2 = j1;j2<=j_max; j2++) {
				for (int k2 = k1; k2<=k_max; k2++)
				{
					curr_vox = &(voxels[i1][j2][k2]);
					trians->p1.x -= curr_vox->center.x;
					trians->p1.y -= curr_vox->center.y;
					trians->p1.z -= curr_vox->center.z;

					trians->p2.x -= curr_vox->center.x;
					trians->p2.y -= curr_vox->center.y;
					trians->p2.z -= curr_vox->center.z;

					trians->p3.x -= curr_vox->center.x;
					trians->p3.y -= curr_vox->center.y;
					trians->p3.z -= curr_vox->center.z;

					if (triangleVoxelIntersection(trians, curr_vox))
					{
						//curr_vox->triangle_indexes.push_back(&trians);
						tmpList[i1][j2][k2].push_back(ind);
					}

					trians->p1.x += curr_vox->center.x;
					trians->p1.y += curr_vox->center.y;
					trians->p1.z += curr_vox->center.z;

					trians->p2.x += curr_vox->center.x;
					trians->p2.y += curr_vox->center.y;
					trians->p2.z += curr_vox->center.z;

					trians->p3.x += curr_vox->center.x;
					trians->p3.y += curr_vox->center.y;
					trians->p3.z += curr_vox->center.z;
				}
			}
		}
	}

	list<int>::iterator it;
	for (int i=0; i<mx; i++) {
		for (int j=0; j<my; j++) {
			for (int k=0; k<mz; k++) {
				if (!tmpList[i][j][k].empty()) {
					voxels[i][j][k].empty = false;
					voxels[i][j][k].trianList = new Triangle*[tmpList[i][j][k].size()];
					voxels[i][j][k].trian_num = tmpList[i][j][k].size();
					int index = 0;
					for (it = tmpList[i][j][k].begin(); it!= tmpList[i][j][k].end(); it++)
					{
						voxels[i][j][k].trianList[index] = triangles+*it;
						index++;
					}
				} else {
					voxels[i][j][k].empty = true;
					voxels[i][j][k].trian_num = 0;
				}
			}
		}
	}

	/*for (int i=0; i<mx; i++) {
		for (int j=0; j<my; j++) {
			for (int k=0; k<mz; k++) {
				if (!tmpList[i][j][k].empty()) {
					for (int index = 0; index<voxels[i][j][k].trian_num; index++)
					{
						cout << *(voxels[i][j][k].trianList[index]);
					}
				}
			}
		}
	}*/
	//int count = 0;
	//for (int i=0; i<mx; i++) for (int j=0; j<my; j++) for (int k=0; k<mz; k++)
	//{
	//	//if (voxels[i][j][k].triangle_indexes.size()>0) { 
	//		cout << i << " " << j << " " << k << " " << voxels[i][j][k].triangle_indexes.size() << endl;
	//		count += voxels[i][j][k].triangle_indexes.size();
	//		//for (list<int>::iterator it = voxels[i][j][k].triangle_indexes.begin(); it != voxels[i][j][k].triangle_indexes.end(); it++)
	//			//cout << (*it) << endl;
	//	//}
	//}
	//cout << mx << " " << my << " " << mz << endl << count;
	/*cout << voxels[8][2][0].center;
	cout << voxels[8][2][0].triangle_indexes.size() << endl;
	cout << voxels[8][2][1].center;
	cout << voxels[8][2][1].triangle_indexes.size() << endl;
	cout << voxels[8][2][2].center;
	cout << voxels[8][2][2].triangle_indexes.size() << endl;
	cout << voxels[8][2][3].center;
	cout << voxels[8][2][3].triangle_indexes.size() << endl;
	cout << voxels[8][2][4].center;
	cout << voxels[8][2][4].triangle_indexes.size() << endl;
	cout << voxels[8][2][5].center;
	cout << voxels[8][2][5].triangle_indexes.size() << endl;
	cout << voxels[8][2][6].center;
	cout << voxels[8][2][6].triangle_indexes.size() << endl;
	cout << voxels[8][2][7].center;
	cout << voxels[8][2][7].triangle_indexes.size() << endl;
	cout << voxels[8][2][8].center;
	cout << voxels[8][2][8].triangle_indexes.size() << endl;
	cout << voxels[8][2][9].center;
	cout << voxels[8][2][9].triangle_indexes.size() << endl;
	cout << voxels[8][2][10].center;
	cout << voxels[8][2][10].triangle_indexes.size() << endl;
	cout << voxels[8][2][11].center;
	cout << voxels[8][2][11].triangle_indexes.size() << endl;
	cout << voxels[8][2][12].center;
	cout << voxels[8][2][12].triangle_indexes.size() << endl;
	cout << voxels[8][2][13].triangle_indexes.size() << endl;
	cout << voxels[8][2][13].center;*/
}

bool Scene::triangleVoxelIntersection(const Triangle* const trian, const Voxel* const vox) {
	long p1_in, p2_in, p3_in;
	float d;
	Vector3d norm, vector12, vector13;

	//Sprawdzanie, czy wierzcholki trojkata leza wewnatrz woksela
	if ((p1_in = facePlane(trian->p1))==0) 
		return 1;
	if ((p2_in = facePlane(trian->p2))==0)
		return 1;
	if ((p3_in = facePlane(trian->p3))==0) 
		return 1;

	//Jeśli wszystkie wierzcholki sa na zewnatrz co najmniej jednej
	//tej samej polplaszczyzny, to znaczy ze woksel nie przecina sie
	//z trojkatem
	if ((p1_in & p2_in & p3_in) != 0) return 0;

	//Odrzucanie trojkatow ktore 
	p1_in |= bevel2d(trian->p1) << 8;
	p2_in |= bevel2d(trian->p2) << 8;
	p3_in |= bevel2d(trian->p3) << 8;
	if ((p1_in & p2_in & p3_in) != 0) return 0;

	p1_in |= bevel3d(trian->p1) << 24;
	p2_in |= bevel3d(trian->p2) << 24;
	p3_in |= bevel3d(trian->p3) << 24;
	if ((p1_in & p2_in & p3_in) != 0) return 0;

	if ((p1_in & p2_in) == 0)
		if (checkLine(trian->p1, trian->p2, p1_in|p2_in)) return 1;
	if ((p1_in & p3_in) == 0)
		if (checkLine(trian->p1, trian->p3, p1_in|p3_in)) return 1;
	if ((p2_in & p3_in) == 0)
		if (checkLine(trian->p2, trian->p3, p2_in|p3_in)) return 1;

	vector12.x = trian->p2.x - trian->p1.x;
	vector12.y = trian->p2.y - trian->p1.y;
	vector12.z = trian->p2.z - trian->p1.z;
	vector12.normalize();
	vector13.x = trian->p3.x - trian->p1.x;
	vector13.y = trian->p3.y - trian->p1.y;
	vector13.z = trian->p3.z - trian->p1.z;
	vector13.normalize();

	norm = vector12.crossProduct(vector13, true);

	d = norm.x * trian->p1.x + norm.y * trian->p1.y + norm.z + trian->p1.z;

	return 1;
}

long Scene::facePlane(const Point3d& p) {
	long mask = 0;

	if (p.x - EPS > voxel_half_legths.x)  mask |= 0x01;
	if (p.x + EPS < -voxel_half_legths.x) mask |= 0x02;
	if (p.y - EPS > voxel_half_legths.y)  mask |= 0x04;
	if (p.y + EPS < -voxel_half_legths.y) mask |= 0x08;
	if (p.z - EPS > voxel_half_legths.z)  mask |= 0x10;
	if (p.z + EPS < -voxel_half_legths.z) mask |= 0x20;
	return mask;
}

long Scene::bevel2d(const Point3d& p) {
	long mask = 0;
	float xy = voxel_half_legths.x + voxel_half_legths.y;
	float xz = voxel_half_legths.x + voxel_half_legths.z;
	float yz = voxel_half_legths.y + voxel_half_legths.z;

	if ((p.x + p.y) > xy + EPS) mask |= 0x001;
	if ((p.x - p.y) > xy - EPS) mask |= 0x002;
	if ((-p.x + p.y) > xy + EPS) mask |= 0x004;
	if ((-p.x - p.y) > xy - EPS) mask |= 0x008;
	if ((p.x + p.z) > xz + EPS) mask |= 0x010;
	if ((p.x - p.z) > xz - EPS) mask |= 0x020;
	if ((-p.x + p.z) > xz + EPS) mask |= 0x040;
	if ((-p.x - p.z) > xz - EPS) mask |= 0x080;
	if ((p.y + p.z) > yz + EPS) mask |= 0x100;
	if ((p.y - p.z) > yz - EPS) mask |= 0x200;
	if ((-p.y + p.z) > yz + EPS) mask |= 0x400;
	if ((-p.y - p.z) > yz - EPS) mask |= 0x800;

	return mask;
}

long Scene::bevel3d(const Point3d& p) {
	long mask = 0;
	float xyz = voxel_half_legths.x + voxel_half_legths.y + voxel_half_legths.z;

	if ((p.x + p.y + p.z) > xyz + EPS) mask |= 0x01;
	if ((p.x + p.y - p.z) > xyz + EPS) mask |= 0x02;
	if ((p.x - p.y + p.z) > xyz + EPS) mask |= 0x04;
	if ((p.x - p.y - p.z) > xyz + EPS) mask |= 0x08;
	if ((-p.x + p.y + p.z) > xyz + EPS) mask |= 0x10;
	if ((-p.x + p.y - p.z) > xyz + EPS) mask |= 0x20;
	if ((-p.x - p.y + p.z) > xyz + EPS) mask |= 0x40;
	if ((-p.x - p.y - p.z) > xyz + EPS) mask |= 0x80;

	return mask;
}

long Scene::checkPoint(const Point3d& p1, const Point3d& p2, float alph, long mask) {
	Point3d planePoint;
	planePoint.x = p1.x + alph*(p2.x - p1.x);
	planePoint.y = p1.y + alph*(p2.y - p1.y);
	planePoint.z = p1.z + alph*(p2.z - p1.z);
	return (facePlane(planePoint) & mask);
}

long Scene::checkLine(const Point3d& p1, const Point3d& p2, long mask_diff) {
	if ((0x01 & mask_diff) != 0)
		if (checkPoint(p1, p2, (voxel_half_legths.x-p1.x)/(p2.x - p1.x), 0x3e) == 0) return 1;
	if ((0x02 & mask_diff) != 0)
		if (checkPoint(p1, p2, (-voxel_half_legths.x-p1.x)/(p2.x - p1.x), 0x3d) == 0) return 1;
	if ((0x04 & mask_diff) != 0)
		if (checkPoint(p1, p2, (voxel_half_legths.x-p1.y)/(p2.y - p1.y), 0x3b) == 0) return 1;
	if ((0x08 & mask_diff) != 0)
		if (checkPoint(p1, p2, (-voxel_half_legths.x-p1.y)/(p2.y - p1.y), 0x37) == 0) return 1;
	if ((0x10 & mask_diff) != 0)
		if (checkPoint(p1, p2, (voxel_half_legths.x-p1.z)/(p2.z - p1.z), 0x2f) == 0) return 1;
	if ((0x20 & mask_diff) != 0)
		if (checkPoint(p1, p2, (-voxel_half_legths.x-p1.z)/(p2.z - p1.z), 0x1f) == 0) return 1;
	return 0;
}

void Scene::render() {
	Vector3d vector;
	Vector3d vecV = Vector3d(camera.topLeft, camera.bottomLeft, false);
	Vector3d vecU = Vector3d(camera.topLeft, camera.topRight, false);
	Point3d planePoint = Point3d();
	float len;
	float x, y, z;
	BYTE* rgb_buffer = new BYTE[3*camera.xResolution*camera.yResolution];
	int buf_pos = 0;
	Color color;

	for (float i=0; i<camera.xResolution; i++)
	{
		//cout << (i/camera.xResolution)*100 << "%\n";
		for (float j=0; j<camera.yResolution; j++)
		{
			buf_pos =j*3*camera.xResolution + i*3;

			x = (static_cast<float>(i/(camera.xResolution-1)))*vecU.x + (static_cast<float>(j/(camera.yResolution-1)))*vecV.x;
			y = (static_cast<float>(i/(camera.xResolution-1)))*vecU.y + (static_cast<float>(j/(camera.yResolution-1)))*vecV.y;
			z = (static_cast<float>(i/(camera.xResolution-1)))*vecU.z + (static_cast<float>(j/(camera.yResolution-1)))*vecV.z;

			x += camera.topLeft.x;
			y += camera.topLeft.y;
			z += camera.topLeft.z;
			
			planePoint.x = x;
			planePoint.y = y;
			planePoint.z = z;
			
			//Wektor kierunkowy promienia pierwotnego

			vector.x = planePoint.x - camera.cameraCenter.x;
			vector.y = planePoint.y - camera.cameraCenter.y;
			vector.z = planePoint.z - camera.cameraCenter.z;
			len = vector.length();
			vector.x /= len;
			vector.y /= len;
			vector.z /= len;
			
			//color = calculateColor(camera.cameraCenter, vector, 0, -1);
			color = calculateColorSub(camera.cameraCenter, vector, 0, -1);

			rgb_buffer[buf_pos] = color.red;
			rgb_buffer[buf_pos + 1] = color.green;
			rgb_buffer[buf_pos + 2] = color.blue;
		}
	}

	BYTE* bmpbuf;
	long newsize;

	bmpbuf = convertRGBToBMPBuffer(rgb_buffer, camera.xResolution, camera.yResolution, &newsize);
	bool saving = saveBMP(bmpbuf, camera.xResolution, camera.yResolution, newsize, "WynikPokoj.bmp");
}

Color Scene::calculateColorSub(Point3d& origin, Vector3d& direction, int dpth, int tri) {
	Color outputColor;
	Triangle* tmpTrian;
	Point3d intersection_p;
	float dtx, dty, dtz;
	float tx, ty, tz;
	float t_near = -numeric_limits<float>::infinity(), t_far = numeric_limits<float>::infinity();
	int attrib = -1, trianIndex = -1, i, j, k;
	int di, dj, dk;
	clock_t start, endT;

	outputColor.red = 0;
	outputColor.green = 0;
	outputColor.blue = 0;

	/************************************************************************/
	/* Znajdź pierwszy przecinany woksel. Najpierw nalezy sprawdzic czy     */
	/* punkt początkowy promienia jest poza domeną. Jeśli tak, promień może */
	/* wejść do domeny tylko przez jeden z bocznych wokseli. Jeśli nie,		*/
	/* należy sprawdzić w którym wokselu promień aktualnie się znajduje.	*/
	/* W obu przypadkach trzeba znaleźć w którą promień będzie się poruszał,*/
	/* aby odpowiednio zwiększać lub zmniejszać indeksy wokseli				*/
	/************************************************************************/

	if (origin.x - EPS > x_min_domain && origin.x + EPS < x_max_domain &&
		origin.y - EPS > y_min_domain && origin.y + EPS < y_max_domain && 
		origin.z - EPS > z_min_domain && origin.z + EPS < z_max_domain)
	{
		//Promien rozpoczyna sie wewnatrz domeny
		//współrzędne wokseli wyliczone
		i = (origin.x - x_min_domain)/dx;
		j = (origin.y - y_min_domain)/dy;
		k = (origin.z - z_min_domain)/dz;

		//Wartości tx, ty i tz dla znalezionego woksela
	} else {
		//Promień rozpoczyna sie na zewnątrz domeny
		if (direction.x == 0 && origin.x > x_max_domain && origin.x < x_min_domain) return outputColor;
		if (direction.y == 0 && origin.y > y_max_domain && origin.y < y_min_domain) return outputColor;
		if (direction.z == 0 && origin.z > z_max_domain && origin.z < z_min_domain) return outputColor;
		float t1, t2;
		t1 = (x_min_domain-origin.x)/direction.x;
		t2 = (x_max_domain-origin.x)/direction.x;
		if (t1>t2) {float tmp = t1; t1 = t2; t2 = tmp;}
		if (t1>t_near) t_near = t1;
		if (t2<t_far) t_far = t2;
		if (t_near>t_far) return outputColor;
		if (t_far<0) return outputColor;

		t1 = (y_min_domain-origin.y)/direction.y;
		t2 = (y_max_domain-origin.y)/direction.y;
		if (t1>t2) {float tmp = t1; t1 = t2; t2 = tmp;}
		if (t1>t_near) t_near = t1;
		if (t2<t_far) t_far = t2;
		if (t_near>t_far) return outputColor;
		if (t_far<0) return outputColor;

		t1 = (z_min_domain-origin.z)/direction.z;
		t2 = (z_max_domain-origin.z)/direction.z;
		if (t1>t2) {float tmp = t1; t1 = t2; t2 = tmp;}
		if (t1>t_near) t_near = t1;
		if (t2<t_far) t_far = t2;
		if (t_near>t_far) return outputColor;
		if (t_far<0) return outputColor;

		i = (origin.x + direction.x * t_near - x_min_domain)/dx;
		j = (origin.y + direction.y * t_near - y_min_domain)/dy;
		k = (origin.z + direction.z * t_near - z_min_domain)/dz;

		i -= i==mx?1:0;
		j -= j==my?1:0;
		k -= k==mz?1:0;

		//wartości tx, ty, tz dla znalezionego woksela
	}
	tx = min((voxels[i][j][k].center.x-voxel_half_legths.x-origin.x)/direction.x, (voxels[i][j][k].center.x+voxel_half_legths.x-origin.x)/direction.x);
	ty = min((voxels[i][j][k].center.y-voxel_half_legths.y-origin.y)/direction.y, (voxels[i][j][k].center.y+voxel_half_legths.y-origin.y)/direction.y);
	tz = min((voxels[i][j][k].center.z-voxel_half_legths.z-origin.z)/direction.z, (voxels[i][j][k].center.z+voxel_half_legths.z-origin.z)/direction.z);

	di = direction.x>0?1:-1;
	dj = direction.y>0?1:-1;
	dk = direction.z>0?1:-1;

	//jesli kierunek jest 0 to powinno byc 0?
	dtx = abs(direction.x==0?0:dx/direction.x);
	dty = abs(direction.y==0?0:dy/direction.y);
	dtz = abs(direction.z==0?0:dz/direction.z);

	Triangle** triang;
	Triangle* inter_trian;
	float tx_n, ty_n, tz_n, tmp_t_int = -1, t_out, t_int = -1;
	int ind = -1, atrib = -1;
	while (i<mx && i>-1 && j<my && j>-1 && k<mz && k>-1) {
		tx_n = tx + dtx;
		ty_n = ty + dty;
		tz_n = tz + dtz;
		t_out = (tx_n<ty_n && tx_n<tz_n)?tx_n:(ty_n<tz_n)?ty_n:tz_n;
		t_int = t_out;
		//Szukaj przecięć w aktualnym wokselu
		if (!voxels[i][j][k].empty) {
			triang = voxels[i][j][k].trianList;
			for (int index = 0; index<voxels[i][j][k].trian_num; triang++, index++) {
				tmpTrian = *triang;
				tmp_t_int = -((*tmpTrian).d + ((*tmpTrian).norm.x*origin.x) + ((*tmpTrian).norm.y*origin.y) + 
					((*tmpTrian).norm.z*origin.z));
				tmp_t_int /= ((*tmpTrian).norm).dotProduct(direction);
				if (tmp_t_int + EPS > 0 && tmp_t_int - EPS<t_int && tmpTrian->ind != tri)
				switch (tmpTrian->plane)
				{
					case 0:
						//Rzutujemy na płaszczyznę (Y, Z)
						intersection_p.y = origin.y+direction.y*tmp_t_int;
						intersection_p.z = origin.z+direction.z*tmp_t_int;
						if (intersection_p.y <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
							&& intersection_p.y >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
							&& (tmpTrian->a1 * intersection_p.y + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
							&& (tmpTrian->a2 * intersection_p.y + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
							&& (tmpTrian->a3 * intersection_p.y + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
								//Punkt jest wewnątrz trójkąta
								trianIndex = tmpTrian->ind;
								//inter_trian = tmpTrian;
								t_int = tmp_t_int;
								atrib = tmpTrian->atributeIndex;
						}
						break;
					case 1:
						//Rzutujemy na płaszczyznę (X, Z)
						intersection_p.x = origin.x+direction.x*tmp_t_int;
						intersection_p.z = origin.z+direction.z*tmp_t_int;
						if (intersection_p.x <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
							&& intersection_p.x >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
							&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
							&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
							&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
								//Punkt jest wewnątrz trójkąta
								trianIndex = tmpTrian->ind;
								//inter_trian = tmpTrian;
								t_int = tmp_t_int;
								atrib = tmpTrian->atributeIndex;
						}
						break;
					case 2:
						//Rzutujemy na płaszczyznę (X, Y)
						intersection_p.x = origin.x+direction.x*tmp_t_int;
						intersection_p.y = origin.y+direction.y*tmp_t_int;
						if (intersection_p.x <= tmpTrian->max1 && intersection_p.y <= tmpTrian->max2
							&& intersection_p.x >= tmpTrian->min1 && intersection_p.y >= tmpTrian->min2
							&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.y) + tmpTrian->d1 >= 0  
							&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.y) + tmpTrian->d2 >= 0
							&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.y) + tmpTrian->d3 >= 0) {
								//Punkt jest wewnątrz trójkąta
								trianIndex = tmpTrian->ind;
								//inter_trian = tmpTrian;
								t_int = tmp_t_int;
								atrib = tmpTrian->atributeIndex;
						}
						break;
					default: break;
				}
			}
		}
		//Jeśli znaleziono przecięcie i znajduje się ono w wokselu, wyjdz z petli
		if (t_int<t_out-EPS)
		{
			break;
		}
		//Przejdz do nastepnego woksela
		if (tx_n<ty_n) {
			if (tx_n< tz_n)
			{
				i+=di; tx = tx_n;
			} else
			{
				k+=dk; tz = tz_n;
			}
		} else {
			if (ty_n<tz_n) 
			{
				j+=dj; ty = ty_n;
			} else
			{
				k+=dk; tz = tz_n;
			}
		}
	}

	if (atrib<0)
		return outputColor;


	intersection_p.x = origin.x+direction.x*t_int;
	intersection_p.y = origin.y+direction.y*t_int;
	intersection_p.z = origin.z+direction.z*t_int;

	Vector3d vecLight;
	Vector3d vecObser;
	Vector3d reflectedRey;
	Vector3d vecReflLight;
	float tmpDotPr;
	float r;

	//Znalezione punkt przeciecia, atrybut oraz odbity trojkat
	
	Attrib* atr = attributes+atrib;
	tmpTrian = triangles+trianIndex;

	//cout << camera.cameraCenter << origin << intersection_p << endl;

	if (atr->ks > 0 && dpth<MAX_DEPTH) {
		vecObser = Vector3d(intersection_p, origin, true);
		reflectedRey.x = 2 * tmpTrian->norm.x;
		reflectedRey.y = 2 * tmpTrian->norm.y;
		reflectedRey.z = 2 * tmpTrian->norm.z;

		tmpDotPr = vecObser.dotProduct(tmpTrian->norm);

		reflectedRey.x *= tmpDotPr;
		reflectedRey.y *= tmpDotPr;
		reflectedRey.z *= tmpDotPr;

		reflectedRey.x -= vecObser.x;
		reflectedRey.y -= vecObser.y;
		reflectedRey.z -= vecObser.z;

		reflectedRey.normalize();

		outputColor = outputColor + calculateColorSub(intersection_p, reflectedRey, ++dpth, trianIndex)*atr->ks;
	}

	for (int i = 0; i<lightNumber; i++) {
		vecLight = Vector3d(intersection_p, lights[i].position, false);
		r = vecLight.length();

		vecLight.x /= r;
		vecLight.y /= r;
		vecLight.z /= r;

		if (isVisibleSub(intersection_p, vecLight, r, trianIndex)) {
			vecObser = Vector3d(intersection_p, origin, true);
			//vecReflLight.x = 2 * tmpTrian->norm.x;
			//vecReflLight.y = 2 * tmpTrian->norm.y;
			//vecReflLight.z = 2 * tmpTrian->norm.z;

			//tmpDotPr = vecLight.dotProduct(tmpTrian->norm);

			//vecReflLight.x *= tmpDotPr;
			//vecReflLight.y *= tmpDotPr;
			//vecReflLight.z *= tmpDotPr;

			//vecReflLight.x -= vecLight.x;
			//vecReflLight.y -= vecLight.y;
			//vecReflLight.z -= vecLight.z;

			//vecReflLight.normalize();

			reflectedRey.x = 2 * tmpTrian->norm.x;
			reflectedRey.y = 2 * tmpTrian->norm.y;
			reflectedRey.z = 2 * tmpTrian->norm.z;

			tmpDotPr = vecObser.dotProduct(tmpTrian->norm);

			reflectedRey.x *= tmpDotPr;
			reflectedRey.y *= tmpDotPr;
			reflectedRey.z *= tmpDotPr;

			reflectedRey.x -= vecObser.x;
			reflectedRey.y -= vecObser.y;
			reflectedRey.z -= vecObser.z;

			reflectedRey.normalize();

			outputColor.red += atr->r * atr->kd*(lights[i].r*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].r*255*lights[i].flux*pow(abs(reflectedRey.dotProduct(vecLight)), atr->wg));
			outputColor.green += atr->g * atr->kd*(lights[i].g*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].g*255*lights[i].flux*pow(abs(reflectedRey.dotProduct(vecLight)), atr->wg));
			outputColor.blue += atr->b * atr->kd*(lights[i].b*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].b*255*lights[i].flux*pow(abs(reflectedRey.dotProduct(vecLight)), atr->wg));

			/*outputColor.red += atr->r * atr->kd*(lights[i].r*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].r*255*lights[i].flux*pow(abs(vecReflLight.dotProduct(vecObser)), atr->wg));
			outputColor.green += atr->g * atr->kd*(lights[i].g*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].g*255*lights[i].flux*pow(abs(vecReflLight.dotProduct(vecObser)), atr->wg));
			outputColor.blue += atr->b * atr->kd*(lights[i].b*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].b*255*lights[i].flux*pow(abs(vecReflLight.dotProduct(vecObser)), atr->wg));*/
		}
	}

	//outputColor.red = atr->r;
	//outputColor.green = atr->g;
	//outputColor.blue = atr->b;

	if (outputColor.red>255) outputColor.red = 255;
	if (outputColor.green>255) outputColor.green = 255;
	if (outputColor.blue>255) outputColor.blue = 255;
	if (outputColor.red<0) outputColor.red = 0;
	if (outputColor.green<0) outputColor.green = 0;
	if (outputColor.blue<0) outputColor.blue = 0;

	return outputColor;
}

float Scene::isVisibleSub(Point3d& origin, Vector3d& direction, float dist, int tri) {
	float transp = 1;
	Color outputColor;
	Triangle* tmpTrian;
	Point3d intersection_p;
	float dtx, dty, dtz;
	float tx, ty, tz;
	float t_near = -numeric_limits<float>::infinity(), t_far = numeric_limits<float>::infinity();
	int attrib = -1, trianIndex = -1, i, j, k;
	int di, dj, dk;
	clock_t start, endT;

	outputColor.red = 0;
	outputColor.green = 0;
	outputColor.blue = 0;

	/************************************************************************/
	/* Znajdź pierwszy przecinany woksel. Najpierw nalezy sprawdzic czy     */
	/* punkt początkowy promienia jest poza domeną. Jeśli tak, promień może */
	/* wejść do domeny tylko przez jeden z bocznych wokseli. Jeśli nie,		*/
	/* należy sprawdzić w którym wokselu promień aktualnie się znajduje.	*/
	/* W obu przypadkach trzeba znaleźć w którą promień będzie się poruszał,*/
	/* aby odpowiednio zwiększać lub zmniejszać indeksy wokseli				*/
	/************************************************************************/

	if (origin.x + EPS > x_min_domain && origin.x - EPS < x_max_domain &&
		origin.y + EPS > y_min_domain && origin.y - EPS < y_max_domain && 
		origin.z + EPS > z_min_domain && origin.z - EPS < z_max_domain)
	{
		//Promien rozpoczyna sie wewnatrz domeny
		//współrzędne wokseli wyliczone
		i = (origin.x - x_min_domain)/dx;
		j = (origin.y - y_min_domain)/dy;
		k = (origin.z - z_min_domain)/dz;

		//Wartości tx, ty i tz dla znalezionego woksela
	} else {
		//Promień rozpoczyna sie na zewnątrz domeny
		if (direction.x == 0 && origin.x > x_max_domain && origin.x < x_min_domain) return 0;// outputColor;
		if (direction.y == 0 && origin.y > y_max_domain && origin.y < y_min_domain) return 0;//outputColor;
		if (direction.z == 0 && origin.z > z_max_domain && origin.z < z_min_domain) return 0;//outputColor;
		float t1, t2;
		t1 = (x_min_domain-origin.x)/direction.x;
		t2 = (x_max_domain-origin.x)/direction.x;
		if (t1>t2) {float tmp = t1; t1 = t2; t2 = tmp;}
		if (t1>t_near) t_near = t1;
		if (t2<t_far) t_far = t2;
		if (t_near>t_far) return 0;//outputColor;
		if (t_far<0) return 0;//outputColor;

		t1 = (y_min_domain-origin.y)/direction.y;
		t2 = (y_max_domain-origin.y)/direction.y;
		if (t1>t2) {float tmp = t1; t1 = t2; t2 = tmp;}
		if (t1>t_near) t_near = t1;
		if (t2<t_far) t_far = t2;
		if (t_near>t_far) return 0;//outputColor;
		if (t_far<0) return 0;//outputColor;

		t1 = (z_min_domain-origin.z)/direction.z;
		t2 = (z_max_domain-origin.z)/direction.z;
		if (t1>t2) {float tmp = t1; t1 = t2; t2 = tmp;}
		if (t1>t_near) t_near = t1;
		if (t2<t_far) t_far = t2;
		if (t_near>t_far) return 0;//outputColor;
		if (t_far<0) return 0;//outputColor;

		i = (origin.x + direction.x * t_near - x_min_domain)/dx;
		j = (origin.y + direction.y * t_near - y_min_domain)/dy;
		k = (origin.z + direction.z * t_near - z_min_domain)/dz;

		//wartości tx, ty, tz dla znalezionego woksela
	}
	i -= i==mx?1:0;
	j -= j==my?1:0;
	k -= k==mz?1:0;

	tx = min((voxels[i][j][k].center.x-voxel_half_legths.x-origin.x)/direction.x, (voxels[i][j][k].center.x+voxel_half_legths.x-origin.x)/direction.x);
	ty = min((voxels[i][j][k].center.y-voxel_half_legths.y-origin.y)/direction.y, (voxels[i][j][k].center.y+voxel_half_legths.y-origin.y)/direction.y);
	tz = min((voxels[i][j][k].center.z-voxel_half_legths.z-origin.z)/direction.z, (voxels[i][j][k].center.z+voxel_half_legths.z-origin.z)/direction.z);

	di = direction.x>0?1:-1;
	dj = direction.y>0?1:-1;
	dk = direction.z>0?1:-1;

	//jesli kierunek jest 0 to powinno byc 0?
	dtx = abs(direction.x==0?0:dx/direction.x);
	dty = abs(direction.y==0?0:dy/direction.y);
	dtz = abs(direction.z==0?0:dz/direction.z);

	//origin.x += 10e-4 * triangles[tri].norm.x;
	//origin.y += 10e-4 * triangles[tri].norm.y;
	//origin.z += 10e-4 * triangles[tri].norm.z;

	Triangle** triang;
	Triangle* inter_trian;
	float tx_n, ty_n, tz_n, tmp_t_int = -1, t_out = -1, t_int = -1;
	int ind = -1, atrib = -1;
	while (i<mx && i>-1 && j<my && j>-1 && k<mz && k>-1) {
		tx_n = tx + dtx;
		ty_n = ty + dty;
		tz_n = tz + dtz;
		t_out = (tx_n<ty_n && tx_n<tz_n)?tx_n:(ty_n<tz_n)?ty_n:tz_n;
		//if (t_out>dist) return transp;
		t_int = t_out;
		//Szukaj przecięć w aktualnym wokselu
		if (!voxels[i][j][k].empty) {
			triang = voxels[i][j][k].trianList;
			for (int index = 0; index<voxels[i][j][k].trian_num; triang++, index++) {
				tmpTrian = *triang;
				tmp_t_int = -((*tmpTrian).d + ((*tmpTrian).norm.x*origin.x) + ((*tmpTrian).norm.y*origin.y) + 
					((*tmpTrian).norm.z*origin.z));
				//tmp_t_int += 10e-6;
				tmp_t_int /= ((*tmpTrian).norm).dotProduct(direction);
				if (tmp_t_int + EPS > 0 && tmp_t_int - EPS < dist /*t_int*/ && tmpTrian->ind != tri)
					switch (tmpTrian->plane)
				{
					case 0:
						//Rzutujemy na płaszczyznę (Y, Z)
						intersection_p.y = origin.y+direction.y*tmp_t_int;
						intersection_p.z = origin.z+direction.z*tmp_t_int;
						if (intersection_p.y <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
							&& intersection_p.y >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
							&& (tmpTrian->a1 * intersection_p.y + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
							&& (tmpTrian->a2 * intersection_p.y + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
							&& (tmpTrian->a3 * intersection_p.y + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
								//Punkt jest wewnątrz trójkąta
								//trianIndex = tmpTrian->ind;
								////inter_trian = tmpTrian;
								//t_int = tmp_t_int;
								//atrib = tmpTrian->atributeIndex;
								return 0;
						}
						break;
					case 1:
						//Rzutujemy na płaszczyznę (X, Z)
						intersection_p.x = origin.x+direction.x*tmp_t_int;
						intersection_p.z = origin.z+direction.z*tmp_t_int;
						if (intersection_p.x <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
							&& intersection_p.x >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
							&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
							&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
							&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
								//Punkt jest wewnątrz trójkąta
								//trianIndex = tmpTrian->ind;
								////inter_trian = tmpTrian;
								//t_int = tmp_t_int;
								//atrib = tmpTrian->atributeIndex;
								return 0;
						}
						break;
					case 2:
						//Rzutujemy na płaszczyznę (X, Y)
						intersection_p.x = origin.x+direction.x*tmp_t_int;
						intersection_p.y = origin.y+direction.y*tmp_t_int;
						if (intersection_p.x <= tmpTrian->max1 && intersection_p.y <= tmpTrian->max2
							&& intersection_p.x >= tmpTrian->min1 && intersection_p.y >= tmpTrian->min2
							&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.y) + tmpTrian->d1 >= 0  
							&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.y) + tmpTrian->d2 >= 0
							&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.y) + tmpTrian->d3 >= 0) {
								////Punkt jest wewnątrz trójkąta
								//trianIndex = tmpTrian->ind;
								////inter_trian = tmpTrian;
								//t_int = tmp_t_int;
								//atrib = tmpTrian->atributeIndex;
								return 0;
						}
						break;
					default: break;
				}
			}
		}
		//Jeśli znaleziono przecięcie i znajduje się ono w wokselu, wyjdz z petli
		if (t_int<t_out-EPS)
		{
			break;
		}
		//Przejdz do nastepnego woksela
		if (tx_n<ty_n) {
			if (tx_n< tz_n)
			{
				i+=di; tx = tx_n;
			} else
			{
				k+=dk; tz = tz_n;
			}
		} else {
			if (ty_n<tz_n) 
			{
				j+=dj; ty = ty_n;
			} else
			{
				k+=dk; tz = tz_n;
			}
		}
	}

	return transp;
}

Color Scene::calculateColor(Point3d& origin, Vector3d& direction, int dpth, int tri) {
	int trianIndex = -1;
	Color outputColor;
	float t_int = numeric_limits<float>::infinity(), tmp_t_int = -1;
	Triangle* tmpTrian = triangles;
	int atrib = -1;
	Point3d intersection_p;

	outputColor.red = 0;
	outputColor.green = 0;
	outputColor.blue = 0;

	//Szukanie najbliższego przeciętego trójką
	for (int ind=0; ind<trianSize; ind++, tmpTrian++)
	{
		tmp_t_int = -((*tmpTrian).d + ((*tmpTrian).norm.x*origin.x) + ((*tmpTrian).norm.y*origin.y) + 
			((*tmpTrian).norm.z*origin.z));
		tmp_t_int /= ((*tmpTrian).norm).dotProduct(direction);

		if (tmp_t_int >=-EPS && tmp_t_int - EPS < t_int && ind != tri) {

			switch (tmpTrian->plane)
			{
			case 0:
				//Rzutujemy na płaszczyznę (Y, Z)
				intersection_p.y = origin.y+direction.y*tmp_t_int;
				intersection_p.z = origin.z+direction.z*tmp_t_int;
				if (intersection_p.y <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
					&& intersection_p.y >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
					&& (tmpTrian->a1 * intersection_p.y + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
					&& (tmpTrian->a2 * intersection_p.y + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
					&& (tmpTrian->a3 * intersection_p.y + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
						//Punkt jest wewnątrz trójkąta
						trianIndex = ind;
						t_int = tmp_t_int;
						atrib = tmpTrian->atributeIndex;
				}
				break;
			case 1:
				//Rzutujemy na płaszczyznę (X, Z)
				intersection_p.x = origin.x+direction.x*tmp_t_int;
				intersection_p.z = origin.z+direction.z*tmp_t_int;
				if (intersection_p.x <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
					&& intersection_p.x >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
					&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
					&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
					&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
						//Punkt jest wewnątrz trójkąta
						trianIndex = ind;
						t_int = tmp_t_int;
						atrib = tmpTrian->atributeIndex;
				}
				break;
			case 2:
				//Rzutujemy na płaszczyznę (X, Y)
				intersection_p.x = origin.x+direction.x*tmp_t_int;
				intersection_p.y = origin.y+direction.y*tmp_t_int;
				if (intersection_p.x <= tmpTrian->max1 && intersection_p.y <= tmpTrian->max2
					&& intersection_p.x >= tmpTrian->min1 && intersection_p.y >= tmpTrian->min2
					&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.y) + tmpTrian->d1 >= 0  
					&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.y) + tmpTrian->d2 >= 0
					&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.y) + tmpTrian->d3 >= 0) {
						//Punkt jest wewnątrz trójkąta
						trianIndex = ind;
						t_int = tmp_t_int;
						atrib = tmpTrian->atributeIndex;
				}
				break;
			default: break;
			}
		}
	}

	//Jeśli punkt nie trafia w żaden trójkąt w domenie, powrót
	if (atrib<0) {
		return outputColor;
	}

	//cout << intersection_p;

	Vector3d vecLight;
	Vector3d vecObser;
	Vector3d reflectedRey;
	Vector3d vecReflLight;
	float tmpDotPr;
	float r;

	//Znalezione punkt przeciecia, atrybut oraz odbity trojkat
	intersection_p.x = origin.x+direction.x*t_int;
	intersection_p.y = origin.y+direction.y*t_int;
	intersection_p.z = origin.z+direction.z*t_int;
	Attrib* atr = attributes+atrib;
	tmpTrian = triangles+trianIndex;

	//cout << camera.cameraCenter << origin << intersection_p << endl;

	if (atr->ks > 0 && dpth<MAX_DEPTH) {
		vecObser = Vector3d(intersection_p, origin, true);
		reflectedRey.x = 2 * tmpTrian->norm.x;
		reflectedRey.y = 2 * tmpTrian->norm.y;
		reflectedRey.z = 2 * tmpTrian->norm.z;

		tmpDotPr = vecObser.dotProduct(tmpTrian->norm);

		reflectedRey.x *= tmpDotPr;
		reflectedRey.y *= tmpDotPr;
		reflectedRey.z *= tmpDotPr;

		reflectedRey.x -= vecObser.x;
		reflectedRey.y -= vecObser.y;
		reflectedRey.z -= vecObser.z;

		reflectedRey.normalize();

		outputColor = outputColor + calculateColor(intersection_p, reflectedRey, ++dpth, trianIndex)*atr->ks;
	}

	for (int i = 0; i<lightNumber; i++) {
		vecLight = Vector3d(intersection_p, lights[i].position, false);
		r = vecLight.length();

		vecLight.x /= r;
		vecLight.y /= r;
		vecLight.z /= r;

		if (isVisible(intersection_p, vecLight, r, trianIndex)) {
			vecObser = Vector3d(intersection_p, origin, true);
			//vecReflLight.x = 2 * tmpTrian->norm.x;
			//vecReflLight.y = 2 * tmpTrian->norm.y;
			//vecReflLight.z = 2 * tmpTrian->norm.z;

			//tmpDotPr = vecLight.dotProduct(tmpTrian->norm);

			//vecReflLight.x *= tmpDotPr;
			//vecReflLight.y *= tmpDotPr;
			//vecReflLight.z *= tmpDotPr;

			//vecReflLight.x -= vecLight.x;
			//vecReflLight.y -= vecLight.y;
			//vecReflLight.z -= vecLight.z;

			//vecReflLight.normalize();

			reflectedRey.x = 2 * tmpTrian->norm.x;
			reflectedRey.y = 2 * tmpTrian->norm.y;
			reflectedRey.z = 2 * tmpTrian->norm.z;

			tmpDotPr = vecObser.dotProduct(tmpTrian->norm);

			reflectedRey.x *= tmpDotPr;
			reflectedRey.y *= tmpDotPr;
			reflectedRey.z *= tmpDotPr;

			reflectedRey.x -= vecObser.x;
			reflectedRey.y -= vecObser.y;
			reflectedRey.z -= vecObser.z;

			reflectedRey.normalize();

			outputColor.red += atr->r * atr->kd*(lights[i].r*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].r*255*lights[i].flux*pow(abs(reflectedRey.dotProduct(vecLight)), atr->wg));
			outputColor.green += atr->g * atr->kd*(lights[i].g*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].g*255*lights[i].flux*pow(abs(reflectedRey.dotProduct(vecLight)), atr->wg));
			outputColor.blue += atr->b * atr->kd*(lights[i].b*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].b*255*lights[i].flux*pow(abs(reflectedRey.dotProduct(vecLight)), atr->wg));

			/*outputColor.red += atr->r * atr->kd*(lights[i].r*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].r*255*lights[i].flux*pow(abs(vecReflLight.dotProduct(vecObser)), atr->wg));
			outputColor.green += atr->g * atr->kd*(lights[i].g*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].g*255*lights[i].flux*pow(abs(vecReflLight.dotProduct(vecObser)), atr->wg));
			outputColor.blue += atr->b * atr->kd*(lights[i].b*lights[i].flux*abs(tmpTrian->norm.dotProduct(vecLight)))/r
				+ atr->ks * (lights[i].b*255*lights[i].flux*pow(abs(vecReflLight.dotProduct(vecObser)), atr->wg));*/
		}
	}

	if (outputColor.red>255) outputColor.red = 255;
	if (outputColor.green>255) outputColor.green = 255;
	if (outputColor.blue>255) outputColor.blue = 255;
	if (outputColor.red<0) outputColor.red = 0;
	if (outputColor.green<0) outputColor.green = 0;
	if (outputColor.blue<0) outputColor.blue = 0;


	return outputColor;
}

float Scene::isVisible(Point3d& origin, Vector3d& direction, float dist, int triang) {
	float transp = 1;
	Point3d intersection_p;
	Triangle* tmpTrian = triangles;
	float tmp_t_int;

	for (int ind=0; ind<trianSize; ind++, tmpTrian++)
	{
		tmp_t_int = -((*tmpTrian).d + ((*tmpTrian).norm.x*origin.x) + ((*tmpTrian).norm.y*origin.y) + 
			((*tmpTrian).norm.z*origin.z));
		tmp_t_int /= ((*tmpTrian).norm).dotProduct(direction);

		if (tmp_t_int >=-EPS && tmp_t_int-EPS < dist && ind != triang/* && ind!=1032 && ind!= 1033*/) {

			switch (tmpTrian->plane)
			{
			case 0:
				//Rzutujemy na płaszczyznę (Y, Z)
				intersection_p.y = origin.y+direction.y*tmp_t_int;
				intersection_p.z = origin.z+direction.z*tmp_t_int;
				if (intersection_p.y <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
					&& intersection_p.y >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
					&& (tmpTrian->a1 * intersection_p.y + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
					&& (tmpTrian->a2 * intersection_p.y + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
					&& (tmpTrian->a3 * intersection_p.y + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
						//Punkt jest wewnątrz trójkąta	
						return 0;
						//atrib = tmpTrian->atributeIndex;
				}
				break;
			case 1:
				//Rzutujemy na płaszczyznę (X, Z)
				intersection_p.x = origin.x+direction.x*tmp_t_int;
				intersection_p.z = origin.z+direction.z*tmp_t_int;
				if (intersection_p.x <= tmpTrian->max1 && intersection_p.z <= tmpTrian->max2
					&& intersection_p.x >= tmpTrian->min1 && intersection_p.z >= tmpTrian->min2
					&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.z) + tmpTrian->d1 >= 0
					&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.z) + tmpTrian->d2 >= 0
					&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.z) + tmpTrian->d3 >= 0) {
						//Punkt jest wewnątrz trójkąta
						return 0;
						//atrib = tmpTrian->atributeIndex;
				}
				break;
			case 2:
				//Rzutujemy na płaszczyznę (X, Y)
				intersection_p.x = origin.x+direction.x*tmp_t_int;
				intersection_p.y = origin.y+direction.y*tmp_t_int;
				if (intersection_p.x <= tmpTrian->max1 && intersection_p.y <= tmpTrian->max2
					&& intersection_p.x >= tmpTrian->min1 && intersection_p.y >= tmpTrian->min2
					&& (tmpTrian->a1 * intersection_p.x + tmpTrian->b1 * intersection_p.y) + tmpTrian->d1 >= 0  
					&& (tmpTrian->a2 * intersection_p.x + tmpTrian->b2 * intersection_p.y) + tmpTrian->d2 >= 0
					&& (tmpTrian->a3 * intersection_p.x + tmpTrian->b3 * intersection_p.y) + tmpTrian->d3 >= 0) {
						//Punkt jest wewnątrz trójkąta
						return 0;
						//atrib = tmpTrian->atributeIndex;
				}
				break;
			default: break;
			}
		}
	}
	return transp;
}

BYTE* Scene::convertRGBToBMPBuffer(BYTE* buff, int width, int height, long* newsize) {
	// first make sure the parameters are valid
	if ( ( NULL == buff ) || ( width == 0 ) || ( height == 0 ) )
		return NULL;

	// now we have to find with how many bytes
	// we have to pad for the next DWORD boundary	

	int padding = 0;
	int scanlinebytes = width * 3;
	while ( ( scanlinebytes + padding ) % 4 != 0 )     // DWORD = 4 bytes
		padding++;
	// get the padded scanline width
	int psw = scanlinebytes + padding;

	// we can already store the size of the new padded buffer
	*newsize = height * psw;

	// and create new buffer
	BYTE* newbuf = new BYTE[*newsize];

	// fill the buffer with zero bytes then we dont have to add
	// extra padding zero bytes later on
	memset ( newbuf, 0, *newsize );

	// now we loop trough all bytes of the original buffer, 
	// swap the R and B bytes and the scanlines
	long bufpos = 0;   
	long newpos = 0;
	for ( int y = 0; y < height; y++ )
		for ( int x = 0; x < 3 * width; x+=3 )
		{
			bufpos = y * 3 * width + x;     // position in original buffer
			newpos = ( height - y - 1 ) * psw + x;           // position in padded buffer

			newbuf[newpos] = buff[bufpos+2];       // swap r and b
			newbuf[newpos + 1] = buff[bufpos + 1]; // g stays
			newbuf[newpos + 2] = buff[bufpos];     // swap b and r
		}

		return newbuf;
}

bool Scene::saveBMP ( BYTE* buff, int width, int height, long paddedsize, LPCTSTR bmpfile )
{
	// declare bmp structures 
	BITMAPFILEHEADER bmfh;
	BITMAPINFOHEADER info;

	// andinitialize them to zero
	memset ( &bmfh, 0, sizeof (BITMAPFILEHEADER ) );
	memset ( &info, 0, sizeof (BITMAPINFOHEADER ) );

	// fill the fileheader with data
	bmfh.bfType = 0x4d42;       // 0x4d42 = 'BM'
	bmfh.bfReserved1 = 0;
	bmfh.bfReserved2 = 0;
	bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paddedsize;
	bmfh.bfOffBits = 0x36;		// number of bytes to start of bitmap bits

	// fill the infoheader

	info.biSize = sizeof(BITMAPINFOHEADER);
	info.biWidth = width;
	info.biHeight = height;
	info.biPlanes = 1;			// we only have one bitplane
	info.biBitCount = 24;		// RGB mode is 24 bits
	info.biCompression = BI_RGB;	
	info.biSizeImage = 0;		// can be 0 for 24 bit images
	info.biXPelsPerMeter = 0x0ec4;     // paint and PSP use this values
	info.biYPelsPerMeter = 0x0ec4;     
	info.biClrUsed = 0;			// we are in RGB mode and have no palette
	info.biClrImportant = 0;    // all colors are important

	// now we open the file to write to
	HANDLE file = CreateFile ( bmpfile , GENERIC_WRITE, FILE_SHARE_READ,
		NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( file == NULL )
	{
		CloseHandle ( file );
		return false;
	}

	// write file header
	unsigned long bwritten;
	if ( WriteFile ( file, &bmfh, sizeof ( BITMAPFILEHEADER ), &bwritten, NULL ) == false )
	{	
		CloseHandle ( file );
		return false;
	}
	// write infoheader
	if ( WriteFile ( file, &info, sizeof ( BITMAPINFOHEADER ), &bwritten, NULL ) == false )
	{	
		CloseHandle ( file );
		return false;
	}
	// write image data
	if ( WriteFile ( file, buff, paddedsize, &bwritten, NULL ) == false )
	{	
		CloseHandle ( file );
		return false;
	}

	// and close file
	CloseHandle ( file );

	return true;
}

void Scene::print() {
	for (int i=0; i<trianSize; i++) {
		cout << triangles[i];
	}
	cout << camera;
}

void Scene::printLight() {
	for (int i=0; i<lightNumber; i++) {
		cout << lights[i];
	}
}

void Scene::printAttributes() {
	for (int i=0; i<attributesNuber; i++) {
		cout << attributes[i] << "\n";
	}
}

void Scene::printMinMax() {
	cout << x_min_domain << " " << y_min_domain << " " << z_min_domain << "\n"
		 << x_max_domain << " " << y_max_domain << " " << z_max_domain << "\n"
		 << domain_width << " " << domain_height << " " << domain_depth << endl;
}